import threading
import time
import datetime
# 这里有错误,不能这样,要确保time.time()调用的是标准库的time模块
# from datetime import time, datetime

from typing import Dict

import psutil
from app.modle.models import Disk, DiskPartition, DiskInfo
from app.utils.util import bytes_to_gb, bytes_to_mb
from app.setting.config import MAX_HISTORY
from app.utils.util import get_current_time
# 磁盘相关信息
class DiskMonitor:
    """磁盘信息监控类，包含统计信息、I/O更新和时间统计"""



    def __init__(self):
        # 初始化历史I/O统计
        self.prev_io_counters = None
        self.last_update_time = time.time()
        # 初始化时间统计
        self.io_time_start = time.time()
        self.read_time_ms = 0.0
        self.write_time_ms = 0.0
        self.busy_time_ms = 0.0
        self.read_list = []
        self.write_list = []
        self.disk_history={
            "读入速度": self.read_list,
            "写入速度": self.write_list
        }
        self.time_history = []
        self._lock = threading.Lock()
        self.last_cache = None  # 新增缓存属性

    def get_disk_usage(self, partition: str = "/") -> Disk:
        """获取指定分区的磁盘使用情况"""
        try:
            usage = psutil.disk_usage(partition) # 获取psutil中的disk_usage函数
            io_counters = psutil.disk_io_counters(perdisk=False)
            now = datetime.datetime.now()
            timestamp = int(now.timestamp())

            # 计算I/O增量
            read_count, write_count, read_bytes, write_bytes = 0, 0, 0, 0
            if self.prev_io_counters:
                time_diff = time.time() - self.last_update_time
                if time_diff > 0:
                    read_count = io_counters.read_count - self.prev_io_counters.read_count
                    write_count = io_counters.write_count - self.prev_io_counters.write_count
                    read_bytes = io_counters.read_bytes - self.prev_io_counters.read_bytes
                    write_bytes = io_counters.write_bytes - self.prev_io_counters.write_bytes

                    # 更新时间统计
                    self._update_io_time_stats(
                        read_count, write_count,
                        read_bytes, write_bytes,
                        time_diff
                    )

            # 保存当前状态用于下次增量计算
            self.prev_io_counters = io_counters
            self.last_update_time = time.time()

            return Disk(
                disk_timestamp=timestamp,
                disk_time=now,
                disk_total=usage.total,
                disk_used=usage.used,
                disk_free=usage.free,
                disk_percentage_usage=round(usage.percent),
                disk_read_count=read_count,
                disk_write_count=write_count,
                disk_read_bytes=read_bytes,
                disk_write_bytes=write_bytes
            )
        except Exception as e:
            # 错误处理：权限问题、挂载点不存在等
            print(f"Error getting disk stats: {e}")
            return Disk()

    def _update_io_time_stats(self, read_count: int, write_count: int,
                              read_bytes: int, write_bytes: int,
                              time_diff: float):
        """更新I/O时间统计信息"""
        # 模拟内核时间统计 (毫秒)
        if read_count > 0 or write_count > 0:
            # 假设每次I/O操作平均耗时 (基于经验值)
            avg_read_time = 5.0  # ms
            avg_write_time = 8.0  # ms

            # 计算读写时间
            self.read_time_ms += read_count * avg_read_time
            self.write_time_ms += write_count * avg_write_time

            # 计算磁盘繁忙时间 (基于吞吐量和操作频率)
            throughput = (read_bytes + write_bytes) / time_diff
            ops_rate = (read_count + write_count) / time_diff

            # 经验公式：繁忙时间 = 基础时间 + 吞吐量因子 + 操作频率因子
            base_time = min(1000 * time_diff, 1000)  # 不超过1000ms
            throughput_factor = throughput / (1024 * 1024) * 50  # 每MB/s增加50ms
            ops_factor = ops_rate / 100 * 10  # 每100IOPS增加10ms

            self.busy_time_ms += base_time + throughput_factor + ops_factor

    def get_io_time_stats(self) -> Dict[str, float]:
        #这里的获取累计IO的时间统计有误做了修改,修正了 get_io_time_stats 方法中的繁忙时间百分比计算
        """获取累积的I/O时间统计信息"""
        total_time = time.time() - self.io_time_start
        busy_percentage = (self.busy_time_ms / (total_time * 1000)) * 100 if total_time > 0 else 0

        return {
            "read_time_ms": round(self.read_time_ms, 2),
            "write_time_ms": round(self.write_time_ms, 2),
            "busy_time_ms": round(self.busy_time_ms, 2),
            "busy_percentage": round(busy_percentage, 1)
        }

        # """获取累积的I/O时间统计信息"""
        # return {
        #     "read_time_ms": round(self.read_time_ms, 2),
        #     "write_time_ms": round(self.write_time_ms, 2),
        #     "busy_time_ms": round(self.busy_time_ms, 2),
        #     "busy_percentage": round(self.busy_time_ms / (time.time() - self.io_time_start) / 10, 1)
        # }

    def get_detailed_stats(self, partition: str = "/") -> Dict:
        """获取详细的磁盘统计信息"""
        disk = self.get_disk_usage(partition)
        time_stats = self.get_io_time_stats()

        return {
            ## "basic": disk.dict(),  # 这种写法已经被淘汰了
            # PydanticDeprecatedSince20: The `dict` method is deprecated; use `model_dump` instead.
            # Deprecated in Pydantic V2.0 to be removed in V3.0. See Pydantic V2 Migration Guide at

            "basic":disk.model_dump(),
            "time_stats": time_stats                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   ,
            "throughput": {
                "read_mbps": round(disk.disk_read_bytes / (1024 * 1024), 2),
                "write_mbps": round(disk.disk_write_bytes / (1024 * 1024), 2)
            },
            "iops": {
                "read_iops": disk.disk_read_count,
                "write_iops": disk.disk_write_count
            }
        }

    import subprocess
    import json


    # linux 专业测试工具
    # def run_fio_test(config_file="fio_test.fio"):
    #     """运行fio测试并解析结果"""
    #     try:
    #         # 执行fio命令（--output-format=json指定输出为JSON格式）
    #         result = subprocess.run(
    #             [
    #                 "fio",
    #                 config_file,
    #                 "--output-format=json"
    #             ],
    #             capture_output=True,
    #             text=True,
    #             check=True
    #         )
    #
    #         # 解析JSON输出
    #         return json.loads(result.stdout)
    #     except subprocess.CalledProcessError as e:
    #         print(f"Fio执行失败: {e.stderr}")
    #         return None
    #     except json.JSONDecodeError as e:
    #         print(f"解析结果失败: {e}")
    #         return None
    #
    # # 示例：创建配置文件并运行测试
    # with open("fio_test.fio", "w") as f:
    #     f.write("""
    # [global]
    # ioengine=libaio
    # direct=1
    # size=1G
    # bs=4k
    # runtime=30
    # numjobs=1
    # group_reporting
    #
    # [read_test]
    # rw=randread
    # filename=/tmp/fio_test_file
    #
    # [write_test]
    # rw=randwrite
    # filename=/tmp/fio_test_file
    # """)
    #
    # result = run_fio_test("fio_test.fio")
    # if result:
    #     # 打印关键指标
    #     print(f"随机读取IOPS: {result['jobs'][0]['read']['iops']}")
    #     print(f"随机写入IOPS: {result['jobs'][1]['write']['iops']}")
    #     print(f"随机读取带宽: {result['jobs'][0]['read']['bw'] / 1024:.2f} MB/s")
    #     print(f"随机写入带宽: {result['jobs'][1]['write']['bw'] / 1024:.2f} MB/s")

    async def get_disk_info(self) -> DiskInfo:
        """获取磁盘使用情况"""
        try:
            # 获取根分区信息
            disk_usage = psutil.disk_usage('/')

            # 获取所有磁盘分区
            partitions = []
            for partition in psutil.disk_partitions():
                try:
                    usage = psutil.disk_usage(partition.mountpoint)
                    partitions.append(DiskPartition(
                        device=partition.device,
                        mountpoint=partition.mountpoint,
                        fstype=partition.fstype,
                        total=bytes_to_gb(usage.total),
                        used=bytes_to_gb(usage.used),
                        free=bytes_to_gb(usage.free),
                        percent=round(usage.percent, 1)

                    ))
                except:
                    continue

            # 计算磁盘读取写入速度
            disk_before = psutil.disk_io_counters()
            time.sleep(1)
            disk_after = psutil.disk_io_counters()
            read_speed = round((disk_after.read_bytes - disk_before.read_bytes)/1024/1024, 2)
            write_speed = round((disk_after.write_bytes - disk_before.write_bytes)/1024/1024, 2)

            # 更新历史数据
            with self._lock:
                self.disk_history["读入速度"].append(read_speed)
                self.disk_history["写入速度"].append(write_speed)
                self.time_history.append(get_current_time())
                if len(self.disk_history) > MAX_HISTORY:
                    self.disk_history.pop(0)
                    self.time_history.pop(0)

            disk_info = DiskInfo(
                total=bytes_to_gb(disk_usage.total),
                used=bytes_to_gb(disk_usage.used),
                free=bytes_to_gb(disk_usage.free),
                percent=round(disk_usage.percent, 1),
                partitions=partitions,
                system_read_speed = read_speed,
                system_write_speed = write_speed
            )
            self.last_cache = disk_info  # 更新缓存
            return disk_info
        except Exception as e:
            print(f"获取磁盘信息失败: {e}")
            return DiskInfo(
                total=100.0,
                used=60.0,
                free=40.0,
                percent=60.0,
                partitions=[],
                history=[60] * 20
            )



# 使用示例
# if __name__ == "__main__":
#     monitor = DiskMonitor()
#
#     # 这里新增一行分区,方便监控disk_info = monitor.get_disk_usage()
#     # 让分区名作为参数传入，方便监控不同分区
#     partition = "C:\\"
#
#     # 这里添加了一个退出条件,否则只能在终端强制关闭
#     try:
#         while True:
#             # 第一次调用获取基础信息
#             # 测试了下本人的磁盘使用情况,没有问题
#             # Initial disk stats:
#             # Total: 464.63 GB
#             # Used: 402.03 GB
#             # Free: 62.61 GB
#             # Usage: 86 %
#             print("Initial disk stats:")
#
#             # disk_info = monitor.get_disk_usage()
#             # 这里做了优化,目的是方便监控不同分区
#             disk_info = monitor.get_disk_usage(partition)
#
#             print(f"Total: {disk_info.disk_total / (1024 ** 3):.2f} GB")
#             print(f"Used: {disk_info.disk_used / (1024 ** 3):.2f} GB")
#             print(f"Free: {disk_info.disk_free / (1024 ** 3):.2f} GB")
#             print(f"Usage: {disk_info.disk_percentage_usage}%")
#
#             print("________________________________________________________")
#             # 等待一段时间后再次调用
#             print("等待一段时间再次调用")
#             time.sleep(5)
#
#             print("\nAfter 2 seconds:")
#
#             disk_info = monitor.get_disk_usage()
#             print(f"Read bytes: {disk_info.disk_read_bytes}")
#             print(f"Write bytes: {disk_info.disk_write_bytes}")
#
#             # 获取详细统计
#             print("\nDetailed statistics:")
#             detailed_stats = monitor.get_detailed_stats()
#
#             # 下面这行代码不够美化
#             # print(detailed_stats)
#             # 但是美化过程中出现了报错,在 json.dumps 里加一个 default 参数
#             # print(json.dumps(detailed_stats, indent=2, ensure_ascii=False))
#
#             # 这里就是正确调用的示例
#             def json_serial(obj):
#                 """JSON 序列化辅助函数"""
#                 if isinstance(obj, (datetime.datetime, datetime.date)):
#                     return obj.isoformat()
#                 raise TypeError(f"Type {type(obj)} not serializable")
#
#
#             print(json.dumps(detailed_stats, indent=2, ensure_ascii=False, default=json_serial))
#
#             time.sleep(5)
#             print("________________________________________________________")
#
#     except KeyboardInterrupt:
#         print("监控已手动终止。")



if __name__ == '__main__':
    monitor = DiskMonitor()
    print(monitor.get_disk_info().json())



